package com.lzyyj.gmis.base.dao;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.Parameter;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.util.ArrayHelper;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import com.lzyyj.common.BeanUtils;
import com.lzyyj.common.GenericsUtils;
import com.lzyyj.common.Page;
import com.lzyyj.gmis.base.dao.hibernate.HibernateEntityDao;
import com.lzyyj.gmis.base.dao.hibernate.SQLCriterion;

/**
 * 一个基于JPA的通用数据访问对象 (DAO) 提供持久化和搜索支持。 save()、update() 和 delete() 操作的事务控制可以直接支持支持
 * Spring 容器管理的事务管理器或者用户管 理的 Spring 事务管理器。 Each of these methods provides
 * additional information for how to configure it for the desired type of
 * transaction control.
 * 
 * @author lzyyj
 */

public class JpaBaseDAO<T, PK extends Serializable> implements IBaseDao<T, PK> {

	private static final Log log = LogFactory.getLog(JpaBaseDAO.class);
	
	@PersistenceContext
	private EntityManager entityManager;

	public EntityManager getEntityManager() {
		return this.entityManager;
	}

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	/**
	 * JPQL 中使用的模型名称
	 */
	private String modelName = "model";
	
	private boolean aggregateRoot = false;

	private static Map<Class<?>, IBaseDao<?, Serializable>> daoMap = new HashMap<Class<?>, IBaseDao<?, Serializable>>();
	
	/**
	 * 有效记录表达式
	 */
	private String validExpr=null;

	// property constants

	public String getValidExpr() {
		return validExpr;
	}

	public void setValidExpr(String validExpr) {
		this.validExpr = validExpr;
	}

	protected Class<T> entityClass;// DAO所管理的Entity类型.

	public Class<T> getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class<T> entityClass) {
		this.entityClass = entityClass;
	}
	
	private void parseEntityClass(Class<T> entityClass) {
		String description;
		if (entityClass==null){
			this.entityClass = (Class<T>)GenericsUtils.getSuperClassGenricType(getClass(), JpaBaseDAO.class, "T");
			description = "默认构造";
		}else{
			this.entityClass = entityClass;
			
			if (entityClass!=null&&entityClass!=Object.class){
				daoMap.put(entityClass, (JpaBaseDAO<?, Serializable>)this);
			}
			
			description = "带参构造";
		}
		description += aggregateRoot?"聚合根":"";
		Class<PK> pkClass = (Class<PK>)GenericsUtils.getSuperClassGenricType(getClass(), JpaBaseDAO.class, "PK");
		log.debug(getClass().getSimpleName()+" "+description+"，"+" 管理实体: "+this.entityClass+" 主键："+pkClass+" ");
	}
	
	/**
	 * 默认构造函数, 通过继承关系确定管理的实体类
	 */
	public JpaBaseDAO() {
		parseEntityClass(null);
	}

	/**
	 * 带参构造函数，构造时注入管理的实体类
	 * 
	 * @param clazz
	 */
	public JpaBaseDAO(Class<T> clazz) {
		parseEntityClass(clazz);
	}

	/**
	 * 带参构造函数，构造时注入管理的实体类
	 * 
	 * @param clazz
	 */
	public JpaBaseDAO(Class<T> clazz, boolean aggregateRoot) {
		this.aggregateRoot = aggregateRoot;
		parseEntityClass(clazz);
	}

	/**
	 * 对以前还未曾保存过的实体进行初始保存。所有后续的持久化操作应该使用 #update()
	 * 方法。 This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#persist(Object)
	 * EntityManager#persist} operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * PersonDAO.save(entity);
	 * txManager.commit(txn);
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Person entity to persist
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	@Override
	public T save(T entity) {
		log.info("正在保存 " + entityClass.getSimpleName() + " 实例");
		try {
			entityManager.persist(entity);
		} catch (RuntimeException re) {
			throw re;
		}

		return entity;
	}

	@Override
	public void delete(Object entity) throws Exception {
		Assert.notNull(entity);
		entityManager.remove(entity);
	}

	/**
	 * Delete a persistent Person entity. This operation must be performed
	 * within the a database transaction context for the entity's data to be
	 * permanently deleted from the persistence store, i.e., database. This
	 * method uses the {@link javax.persistence.EntityManager#remove(Object)
	 * EntityManager#delete} operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * PersonDAO.delete(entity);
	 * txManager.commit(txn);
	 * entity = null;
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Person entity to delete
	 * @throws Exception 
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	@Override
	public void deleteById(PK id) {
		log.info("deleting " + entityClass.getSimpleName() + " instance");
		T entity = entityManager.getReference(entityClass, id);
		try {
			delete(entity);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		log.info("delete successful");
	}

	@Override
	public void deleteByIds(PK[] ids) {
		for (PK id:ids){
			deleteById(id);
		}
	}

	/**
	 * Persist a previously saved Person entity and return it or a copy of it to
	 * the sender. A copy of the Person entity parameter is returned when the
	 * JPA persistence mechanism has not previously been tracking the updated
	 * entity. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge}
	 * operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * entity = PersonDAO.update(entity);
	 * txManager.commit(txn);
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Person entity to update
	 * @return Person the persisted Person entity instance, may not be the same
	 * @throws RuntimeException
	 *             if the operation fails
	 */
	@Override
	public T update(T entity) {
		log.info("updating " + entityClass.getSimpleName() + " instance");
		try {
			T result = entityManager.merge(entity);
			log.info("update successful");
		} catch (RuntimeException re) {
			log.error("update failed", re);
			throw re;
		}
		return entity;
	}

	public T saveOrUpdate(T entity) {
		if (this.getPK(entity)==null){
			this.save(entity);
		}else{
			this.update(entity);
		}
		return entity;
	}

	@Override
	public void executeUpdate(String jpql, Object... values) {
	    String updateHql = "update "+entityClass.getSimpleName()+(jpql.indexOf("set ")>=0?" ":" set ") + jpql; 
	    Query query = createQuery(updateHql, values);
	    query.executeUpdate();
	}

	@Override
	public T get(T entity) {
		return getById(this.getPK(entity));
	}

	@Override
	public T getById(PK id) {
		log.info("finding " + entityClass.getSimpleName()
				+ " instance with id: " + id);
		try {
			T instance = entityManager.find(entityClass, id);
			return instance;
		} catch (RuntimeException re) {
			log.error("find failed", re);
			throw re;
		}
	}

	@Override
	public T load(PK id) throws Exception {
		return getById(id);
	}

	@Override
	public long getAllCount() {
		long count = 0;
		String queryString = "select count(*) from " + entityClass.getSimpleName() + " ";
		Query query = entityManager.createQuery(queryString);
		Object result = query.getResultList().get(0);
		if (result!=null){
			if (result instanceof Long){
				count = ((Long)result).longValue();
			}else if(result instanceof Integer){
				count = ((Integer)result).longValue();
			}
		}
		return count;
	}

	@Override
	public long getCount(Map<String, Object> condition) {
		String jpql = "select count(*) from "+entityClass.getSimpleName()+" ";
		String jpqlCondition = (String)condition.get(IBaseDao.JPQL_CONDITION);
		Object[] values = (Object[])condition.get(IBaseDao.VALUES);
		if (jpqlCondition!=null){
			jpql += " where "+jpqlCondition;
		}
		Query query = entityManager.createQuery(jpql);
		for(int i=0;i<values.length;i++){
			Object paramValue = values[i];
			query.setParameter(i, paramValue);
		}
		
		return (Long)(query.getResultList().get(0));
	}

	@Override
	public long getCount(Page<T> page) {
		return getCount(page.getCondition());
	}

	@Override
	public boolean isUnique(T entity, String propertyList) {
	    Assert.hasText(propertyList); 
	    long count;
	    String queryString = "select count(*) from " + entityClass.getSimpleName() + " as " + modelName + " where ";
	    String[] nameList = propertyList.split(","); 
	    StringBuffer condition = new StringBuffer();
	    List<Object> params = new ArrayList<Object>();
	    try { 
	        // 循环加入唯一列 
	        for (String name : nameList) {
	        	Object value = PropertyUtils.getProperty(entity, name);
	        	if (value==null){
	        		condition.append(modelName).append(".").append(name).append(" is null"); 
	        	}else{
	        		condition.append(modelName).append(".").append(name).append(" = ?");
	        		params.add(value);
	        	}
	        } 
	
	        // 以下代码为了如果是update的情况,排除entity自身. 
	
	        String idName = getPKName(); 
	
	        // 取得entity的主键值 
	        PK id = getPK(entity); 
	
	        // 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断 
	        if (id != null){
	        	condition.append(modelName).append(".").append(idName).append(" <> :" + idName);
	        	params.add(id);
	        }
	        String hql = condition.toString();
	        Query query = entityManager.createQuery(hql);
	        for(int i=0;i<params.size();i++){
	        	Object value = params.get(i);
	        	query.setParameter(i, value);
	        }
	        count = (Integer)query.getSingleResult();
		    return count == 0; 
	        
	    } catch (Exception e) { 
	        ReflectionUtils.handleReflectionException(e); 
	        return false;
	    } 
	}

	/**
	 * Find all Person entities with a specific property value.
	 * 
	 * @param propertyName
	 *            the name of the Person property to query
	 * @param value
	 *            the property value to match
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            number of results to return.
	 * @return List<Person> found by query
	 */
	public List<T> findByProperty(String propertyName, final Object value, final long... rowStartIdxAndCount) {
		log.info("finding " + entityClass.getSimpleName() + " instance with property: " + propertyName + ", value: " + value);
		// 是否要忽略有效性检查
		boolean ignoreValidState = (validExpr==null);
		final Map<String, Object> paramMap = new HashMap<String, Object>();
		final Map<String, String> joinMap = new HashMap<String, String>();
		int joinId = 0;
		paramMap.put(propertyName, value);
		StringBuffer condition = new StringBuffer("");
		try {
			setCondition1(propertyName, value, condition, paramMap, joinMap, joinId);

			final String queryString = getJpql("", ignoreValidState, joinMap, condition);
			
			Query query = entityManager.createQuery(queryString);
			for (String propertyName : paramMap.keySet()) {
				query.setParameter(propertyName, paramMap.get(propertyName));
			}
			if (rowStartIdxAndCount != null
					&& rowStartIdxAndCount.length > 0) {
				long rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
				if (rowStartIdx > 0) {
					query.setFirstResult((int)rowStartIdx);
				}

				if (rowStartIdxAndCount.length > 1) {
					long rowCount = Math.max(0, rowStartIdxAndCount[1]);
					if (rowCount > 0) {
						query.setMaxResults((int)rowCount);
					}
				}
			}
			return query.getResultList();

		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}

	/**
	 * Find all Entity entities with a specific property value.
	 * 
	 * @param propertyName
	 *            the name of the Entity property to query
	 * @param value
	 *            the property value to match
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            count of results to return.
	 * @return List<Entity> found by query
	 * @throws Exception 
	 */
	@Override
	public List<T> findByExample(final T example, String propertiesList, final String orderFields, final long... rowStartIdxAndCount) throws Exception {
		log.info("正在按属性列表的指示(" + propertiesList + ")，查找类似的 " + entityClass.getSimpleName() + " 实例: , 参考对象为: " + example.toString());
		
		// 是否要忽略有效性检查
		boolean ignoreValidState = (validExpr==null);

		try {
			final Map<String, Object> params = new HashMap<String, Object>();
			final Map<String, String> joinMap = new HashMap<String, String>();
			int joinId = 0;
			String[] propertys = propertiesList.split(",");
			StringBuffer condition = new StringBuffer("");

			for (String propertyName : propertys) {
				if ("*".equals(propertyName)) {
					// 包含其它非空属性
				} else if (propertyName.length()>0) {
					if (condition.length() > 0) {
						condition.append(" and ");
					}
					Object value = PropertyUtils.getProperty(example, propertyName);
					setCondition1(propertyName, value, condition, params, joinMap, joinId);
				}
			}
			
			final String queryString = getJpql(orderFields, ignoreValidState, joinMap, condition);
			
			return entityManager.executeFind(executeFind(queryString, params, rowStartIdxAndCount));
			
		} catch (Exception re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}

	/**
	 * 返回与给定示例的指定的属性相符的对象列表
	 * 
	 * @param propertyList
	 *            在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
	 * @throws Exception 
	 */
	public List<T> findByExample(T entity, String propertyList, String orderBy, String propertyList2, long... firstAndMaxResults) throws Exception {
		List<T> list;
		
		StringBuffer hql = new StringBuffer("");

		addExampleCriteria(hql, entity, null, propertyList);

		this.addOrder(hql, orderBy);
		
		Query query = entityManager.createQuery(arg0);

		addFirstAndMaxResults(criteria, firstAndMaxResults);
		
		list = (List<T>) criteria.list();
		
		loadProperties(list, entityClass, propertyList2, null);

		return list;
	}

	/**
	 * 根据示例对象及其他条件构造查询条件
	 * 
	 * @param criteria
	 * @param example
	 *            示例对象，存放有参考值
	 * @param condition
	 *            存放条件
	 * @param propertyList
	 *            要查询的属性名列表
	 * @return
	 */
	private void addExampleCriteria(StringBuffer criteria, T example, Map<String, Object> condition, String propertyList) {
		if (criteria == null)
			criteria = new StringBuffer();

		// 实体外键映射表，key为属性访问路径，value 为相应层级对象映射别名
		Map<String, String> entityAlias = new HashMap<String, String>();
//		entityAlias.put(aliasName, entityClass);
		
		// 别名-实体映射表, key 为别名，value 为映射的实体类
		Map<String, Class<?>> aliasMap = new HashMap<String, Class<?>>();

		ClassMetadata meta = entityManager..getClassMetadata(entityClass);
		Map<String, Object> propertyTypes = new HashMap<String, Object>();
		for (PropertyDescriptor property : PropertyUtils.getPropertyDescriptors(entityClass)) {
			propertyTypes.put(property.getName(), property.getPropertyType());
		}

		String[] propertyArray;
		// 是否有有效表达式相关属性
		boolean haveRefProp = false;

		// 是否有指定的属性列表
		if (propertyList == null || propertyList.trim().length() == 0) {
			// 没有，根据有值的属性构造查询条件
			// 所有属性的列表
			propertyArray = meta.getPropertyNames();
		} else {
			// 有指定的属性列表，根据属性列表构造查询条件
			propertyArray = propertyList.split(",");
		}

		try {
			// 遍历属性列表,按属性加入条件
   			for (String propertyName : propertyArray) {
				Object value = null;

				if (example != null) {
					Boolean inverse = false;
					
					// 字符串比较使用like 
					Boolean like = true;
					
					// 字符串比较使用多处like，关键字用空格分隔
					Boolean multiLike = false;
					
					// 字符串比较仅从开始处比较
					Boolean onlyBegin = true;
					
					propertyName = parserCompareMode(propertyName, inverse , like, multiLike, onlyBegin);
					
					List<Class<?>> valueClasses = new ArrayList<Class<?>>();
					
					// 解析出相关持久化属性值，属性名及属性类型
					value = getPersistentPropertyValue(example, propertyName, entityClass, aliasMap, valueClasses, entityAlias, criteria);
					String persistentProperty = entityAlias.get(aliasName+"."+propertyName);
					Class<?> valueClass = valueClasses.get(valueClasses.size()-1);
					
					if (propertyName!=null){
						if (value == null || value instanceof String && ((String) value).length() == 0) {
							// 参考值为空或空串
							if (condition!=null&&condition.containsKey(propertyName)){
								// 条件集合中有附加条件
								value = condition.get(propertyName);
								if (value instanceof List){
									
								}
							}else{
								// 没有附加条件, 看属性列表是否是明确指定的
								if (propertyList != null && propertyList.trim().length() > 0) {
									// 属性列表非空，即为明确指定要比较的属性，必须作为条件
									if (inverse) {
										criteria.add(Restrictions.isNotNull(persistentProperty));
									} else {
										if (valueClass .equals(Integer.class) ){
											criteria.add(Restrictions.or(Restrictions.isNull(persistentProperty), Restrictions.eq(persistentProperty, 0)));
										}else{
											criteria.add(Restrictions.isNull(persistentProperty));
										}
									}
								}
							}
						} else {
							// 有值
							if (value instanceof String) {
								// 字符串值
								if (like){
									// like比较
									if (onlyBegin){
										// 仅起始位置比较
										if (inverse){
											criteria.add(Restrictions.not(Restrictions.like(persistentProperty, value + "%")));
										}else{
											criteria.add(Restrictions.like(persistentProperty, value + "%"));
										}
									}else{
										String strValue;
										 if (multiLike){
												// 多关键字比较
												strValue = ((String)value).replaceAll(" ", "%");
												strValue = strValue.replaceAll("%%", "%");
										 }else{
											 strValue = (String)value;
										 }
											if (inverse){
												criteria.add(Restrictions.not(Restrictions.like(persistentProperty, "%"+ strValue + "%")));
											}else{
												criteria.add(Restrictions.like(persistentProperty, "%"+ strValue + "%"));
											}
									}
								}else{
									// 非like比较
									if (inverse) {
										criteria.add(Restrictions.not(Restrictions.eq(persistentProperty, value)));
									} else {
										criteria.add(Restrictions.eq(persistentProperty, value));
									}
								}
							} else if(value instanceof Collection) {
								// 参考值为集合
								Collection<?> collection = (Collection<?>)value;
								if (collection.size()==0){
									// 集合为空
									if (condition!=null&&condition.containsKey(persistentProperty)){
										// 条件集合中有附加条件
										value = condition.get(persistentProperty);
										if (value instanceof List){
											
										}
									}else{
										// 没有附加条件, 看属性列表是否是明确指定的
										if (propertyList != null && propertyList.trim().length() > 0) {
											// 属性列表非空，即为明确指定要比较的属性，必须作为条件
											if (inverse) {
												criteria.add(Restrictions.isNotEmpty(persistentProperty));
											} else {
												criteria.add(Restrictions.isEmpty(persistentProperty));
											}
										}
									}
								}else{
									criteria.add(Restrictions.in(persistentProperty, collection));
								}
							}else{
								// 非字符串值、非集合
								if (inverse) {
									criteria.add(Restrictions.not(Restrictions.eq(persistentProperty, value)));
								} else {
									criteria.add(Restrictions.eq(persistentProperty, value));
								}
							}
						}
					}
				}

				// 是否有相应属性的其他条件
				if (condition != null && condition.containsKey(propertyName)) {
					value = condition.get(propertyName);
					if (value != null) {
						if ((value.getClass().isArray() || value instanceof List)) {
							criteria.add(Restrictions.in(propertyName, (List<?>) value));
						} else {
							criteria.add(Restrictions.eq(propertyName, value));
						}
					} else {
						criteria.add(Restrictions.isNull(propertyName));
					}
				}

			}
   			
   			if (haveValidExpressions()&&!haveRefProp){
   				criteria.add(Restrictions.sqlRestriction(aliasName+"."+validExpressions));
   			}
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}

		return criteria;
	}

	/**
	 * 从字段名中解析比较模式并将其分离出来
	 * @param criteria
	 * @param example
	 * @param condition
	 * @param propertyList
	 * @param aliasMap
	 * @param propertyTypes
	 * @param nameString
	 * @param value
	 * @param inverse
	 * @param like
	 * @param multiLike
	 * @param onlyBegin
	 * @return
	 * @throws Exception
	 */
	public static String parserCompareMode(String nameString, Boolean inverse, Boolean like, Boolean multiLike, Boolean onlyBegin) throws Exception {

		if (nameString.startsWith("!")) {
			inverse = true;
			nameString = nameString.substring(1);
		}
		// 对于字符串字段，强调是相等匹配
		if (nameString.startsWith("=")) {
			like = false;
			nameString = nameString.substring(1);
		}
		// 强调是起始要匹配
		if (nameString.startsWith("%")) {
			like = true;
			nameString = nameString.substring(1);
		}
		// 强调是中间有匹配
		if (nameString.endsWith("%%")) {
			like = true;
			multiLike =true;
			onlyBegin = false;
			nameString = nameString.substring(0,nameString.indexOf("%%"));
		}
		// 强调结尾匹配
		if (nameString.endsWith("%")) {
			like = true;
			multiLike = false;
			nameString = nameString.substring(0,nameString.indexOf("%"));
		}
		return nameString;
	}

	/**
	 * 获取持久化的属性值，逐级分析属性描述, 外键关联的添加外键别名,非持久化属性映射成为持久化属性 
	 * @param clazz
	 * @param example
	 * @param propertyName
	 * @param aliasMap
	 * @return
	 * @throws Exception
	 */
	public Object getPersistentPropertyValue(T example, String propertyName, Class<T> clazz, Map<String, Class<?>> aliasMap, List<Class<?>> valueClasses, Map<String, String> entityAlias, Criteria criteria) throws Exception{
		Object persistentPropertyValue = null;
		
		// 当前主键对象实体
		Object pkEntity = example;
		// 当前主键对象类型
		Class<?> pkEntityClass = clazz == null ? example.getClass() : clazz;
		// 当前主键对象别名
		String pkAlias = aliasName;
		// 以根实体别名为起始的属性路径
		String propertyPath = pkAlias;
		// 当前实体类默认别名
		aliasMap.put(aliasName, pkEntityClass);
	
		// 检查属性中的外键关联
		String[] names = propertyName.split("\\.");

		String currPropertyName = null;
		Class<?> fkEntityClass = null;
		Object fkEntity = null;
		String alias = null;
		// 将最后一级之前的各级属性名，逐级解析，并映射为外键对象
		for (int i = 0; i < names.length; i++) {
			if (pkEntity==null){
				// 上级解析出的实体为空，不正确，无法继续解析
				throw new Exception("指定携带查询参数的对象不存在："+propertyPath);
			}

			currPropertyName = names[i];
			String persistentProperty;
			propertyPath = propertyPath+"."+currPropertyName;
			
			// 迭代为下一级关联对象

			fkEntityClass = BeanUtils.getPropertyType(pkEntityClass, currPropertyName);
			fkEntity = PropertyUtils.getProperty(pkEntity, currPropertyName);

			// 如果该属性为集合类型，类型改取其成员类型，值取其第一个成员的值，去不到则为null
			if (Collection.class.isAssignableFrom(fkEntityClass) && fkEntity != null) {
				boolean have = false;
				if (((Collection<?>) fkEntity).size()>0){
					fkEntityClass = BeanUtils.getMemberType(pkEntityClass, currPropertyName);
					// 如果是一个非空集合类型，才需要关联，并且只取一个条目
					for (Object item : (Collection<?>) fkEntity) {
						if (item!=null){
							fkEntity = item;
							have = true;
							break;
						}
					}
				}
				if (!have)fkEntity = null;
			}
			
			if (isEntity(fkEntityClass)){
				// 关联对象是实体
				log.debug("关联对象是实体");
				if (isPersistentProperty(currPropertyName)){
					// 是一个直接关联了的外键对象
					log.debug("属性是持久化属性");
					
					// 当前属性就是持久化属性
					persistentProperty = currPropertyName;

					// 还有下一级吗？
					if (i<names.length-1){
						// 这不是最后一级，必添加别名
						if (entityAlias.containsKey(propertyPath)){
							// 该级外键已经处理过
							alias = entityAlias.get(propertyPath);
						}else{
							// 获取一个不会冲突的新别名
							alias = getNewAlias(currPropertyName, aliasMap);
							String associationPath = propertyPath;
							
							criteria.createAlias(associationPath, alias);
							
							aliasMap.put(alias, fkEntityClass);
							entityAlias.put(propertyPath, alias);
						}
					}
				}else{
					log.debug("属性是非持久化的外键对象属性");
					// 查找外键关联记录
					Entry<String, Class<?>> entry = PersistenceHelper.findForeignKey(currPropertyName, fkEntityClass, foreignKeyMap);
					if (entry!=null){
						String[] keyStr = entry.getKey().split(":");
						if (keyStr.length>1){
							String[] pList = keyStr[1].split("\\.");
							for(int j=0; j<pList.length;j++){
								String currPersistentProperty = pList[j]; // 外键表对象
								if (j<pList.length-1){
									// 中间级
									// 需反向关联,如 DeptUser .deptId
									if (entityAlias.containsKey(propertyPath)){
										// 该级外键已经处理过
										alias = entityAlias.get(propertyPath);
									}else{
										// 获取外键对象类
										Class<?> relationObjClass = BeanUtils.getPropertyType(pkEntityClass, currPersistentProperty);
										
										if (Collection.class.isAssignableFrom(relationObjClass)){
											log.debug("是一个一对多关系表");
											relationObjClass = BeanUtils.getMemberType(pkEntityClass, currPersistentProperty);
										}else{
											log.debug("aa");
										}
										// 未处理过的外键映射，获取一个不会冲突的新别名
										alias = getNewAlias(currPersistentProperty, aliasMap);
										// 当前类，即关系表中到源类外键关系的主键类主键名
										String pkName = getPkNameByEntityClass(pkEntityClass);
										// 关系表中关联源类的外键属性名
										String fk=null;
										// 目标类，即关系表中到目标类外键关系的主键类主键名
										String pk2Name = getPkNameByEntityClass(fkEntityClass);
										// 关系表中关联目标类的外键属性名
										String fk2 = null;
										String validExpressions = null;
										
										// 关系表dao
										HibernateEntityDao<?, Serializable> dao = getBaseDao(relationObjClass);
										
										Entry<String, Class<?>> en = PersistenceHelper.findForeignKey(currPersistentProperty, pkEntityClass, dao.getForeignKeyMap());
										if (en!=null){
											String[] p = en.getKey().split(":");
											if (p.length>1){
												fk = p[1];
											}
										}
										en = PersistenceHelper.findForeignKey(currPersistentProperty, fkEntityClass, dao.getForeignKeyMap());
										if (en!=null){
											String[] p = en.getKey().split(":");
											if (p.length>1){
												fk2 = p[1];
											}
										}
										
										validExpressions = dao.getValidExpressions();
										
										if (fkEntityClass.equals(dao.getEntityClass())){
											// 应该不可能
											log.debug("应该不可能！");
										}else{
											log.debug("应该是一个关系表："+dao.getEntityClass());
											// 查找到目标类型的外键属性
											// 如果下一级就是最后一级，并且属性名是主机名，则持久化属性换成其对应的外键属性名
											if (i==(names.length-2)&&names[names.length-1].equals(pk2Name)){
												entityAlias.put(aliasName+"."+propertyName, alias+"."+fk2);
											}
										}
										
										log.debug("添加别名："+propertyPath+" = "+currPersistentProperty+" : "+alias);
										criteria.createAlias(pkAlias + "." + currPersistentProperty, alias);

										if (validExpressions!=null&&validExpressions.length()>0){
											// 目前仅支持一字段名的表达式，并假定是在开始处
											criteria.add(new SQLCriterion("{"+alias+"}"+"."+validExpressions, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY));
//											criteria.add(Restrictions.sqlRestriction(alias+"."+validExpressions));
										}
										
										aliasMap.put(alias, relationObjClass);
										entityAlias.put(propertyPath, alias);
									}
								}else{
									persistentProperty = currPersistentProperty;
									// 主属性末级
									// 应是直接的外键属性，如parId、deptUsers
									if (i==names.length-1){
										// 末级属性
										// 映射后的类型，应该是主键类型
										String fkIdName = getPkNameByEntityClass(fkEntityClass);
										fkEntityClass = BeanUtils.getPropertyType(fkEntityClass, fkIdName);
										if (pkEntity!=null){
											fkEntity = (Serializable)PropertyUtils.getProperty(fkEntity, fkIdName);
										}
										entityAlias.put(aliasName+"."+propertyName, pkAlias+"."+persistentProperty);
									}else{
										// 非末级属性
										log.debug("注意：应是直接的外键属性，如parId, 中间级！");
										if (entityAlias.containsKey(propertyPath)){
											// 该级外键已经处理过
											alias = entityAlias.get(propertyPath);
										}else{
											// 获取外键对象类
											Class<?> relationObjClass = BeanUtils.getPropertyType(pkEntityClass, currPersistentProperty);
											
											
											// 未处理过的外键映射，获取一个不会冲突的新别名
											alias = getNewAlias(currPropertyName, aliasMap);
											// 当前类，即关系表中到源类外键关系的主键类主键名
											String pkName = getPkNameByEntityClass(pkEntityClass);
											// 关系表中关联源类的外键属性名
											String fk=null;
											// 目标类，即关系表中到目标类外键关系的主键类主键名
											String pk2Name = getPkNameByEntityClass(fkEntityClass);
											// 关系表中关联目标类的外键属性名
											String fk2 = null;
											String validExpressions = null;
											
											// 关系表dao
											HibernateEntityDao<?, Serializable> dao = getBaseDao(relationObjClass);
											
											Map<String, Class<?>> foreignKeyMap = dao.getForeignKeyMap();
											if (foreignKeyMap!=null){
												for(Entry<String, Class<?>> e:foreignKeyMap.entrySet()){
													if (e.getValue().equals(pkEntityClass)){
														log.debug("dd");
														fk = e.getKey();
													}
													if (e.getValue().equals(fkEntityClass)){
														fk2 = e.getKey();
													}
												}
											}
											validExpressions = dao.getValidExpressions();
											
											if (fkEntityClass.equals(dao.getEntityClass())){
												// 应该不可能
												log.debug("应该不可能！");
											}else{
												log.debug("应该是一个关系表："+dao.getEntityClass());
												// 查找到目标类型的外键属性
												entityAlias.put(propertyPath+"."+pk2Name, currPersistentProperty);
											}
											
											log.debug("添加别名："+propertyPath+" = "+propertyPath+" : "+alias);
											criteria.createAlias(currPersistentProperty, alias, CriteriaSpecification.LEFT_JOIN);
											
											if (fk!=null){
												log.debug("添加关联条件："+alias+"."+fk+" = "+pkAlias+"."+pkName);
												criteria.add(Restrictions.eq(alias+"."+fk, pkAlias+"."+pkName));
											}

											if (validExpressions!=null){
												criteria.add(Restrictions.sqlRestriction(validExpressions));
											}
											
											aliasMap.put(alias, relationObjClass);
											entityAlias.put(propertyPath, alias);
										}
									}
								}
								if (alias!=null){
									pkAlias = alias;
									alias = null;
								}
							}
						}
					}
				}
			}else{
				if (i!=names.length-1){
					// 中间级，非实体,不应该哦， Ingeger，Sting，不可能
					log.debug("关联对象不是是实体");
					throw new Exception("中间级对象，非实体对象！！！");
				}
			}

			if (alias!=null){
				pkAlias = alias;
				alias = null;
			}

			if (valueClasses!=null){
				valueClasses.add(fkEntityClass);
			}
			
			// 非最后一轮，变量迭代
			if (i<names.length-1){
				pkEntity = fkEntity;
				pkEntityClass = fkEntityClass;
				fkEntity = null;
				fkEntityClass = null;
			}
		}
		
		persistentPropertyValue = fkEntity;
		
		// 如果中途没有使用替代属性
		if (!entityAlias.containsKey(aliasName+"."+propertyName)){
			entityAlias.put(aliasName+"."+propertyName, pkAlias+"."+currPropertyName);
		}
		
		return persistentPropertyValue;
	}

	/**
	 * 按实体名检索dao
	 * @param entityName
	 * @return
	 */
	private HibernateEntityDao<?, Serializable> getDao(String entityName) {
		HibernateEntityDao<?, Serializable> dao = null;
		for(Entry<Class<?>, HibernateEntityDao<?, Serializable>> daoEntry:daoMap.entrySet()){
			if (daoEntry.getKey().getSimpleName().equalsIgnoreCase(entityName)){
				dao = daoEntry.getValue();
				break;
			}
		}
		return dao;
	}

	private String getNewAlias(String currPropertyName, Map<String, Class<?>> aliasMap) {
		String newAlias = currPropertyName;
		
		int i = 0;
		while (aliasMap.containsKey(newAlias)){
			newAlias = currPropertyName + "_" + (++i); 
		}
		
		return newAlias;
	}

	private Criteria addPropertyCriteria(Criteria criteria, String propertyName, Object value) {
		if (criteria == null)
			criteria = createCriteria();
		if (value == null) {
			criteria.add(Restrictions.isNull(propertyName));
		} else {
			criteria.add(Restrictions.eq(propertyName, value));
		}
		return criteria;
	}

	private Criteria addFirstAndMaxResults(Criteria criteria, long... firstAndMaxResults) {
		if (criteria == null)
			criteria = createCriteria();

		if (firstAndMaxResults.length > 0) {
			criteria.setFirstResult((int) firstAndMaxResults[0]);
			if (firstAndMaxResults.length > 1) {
				criteria.setMaxResults((int) firstAndMaxResults[1]);
			}
		}
		return criteria;
	}

	public Criteria addOrder(Criteria criteria, String orderBy) {
		if (criteria == null)
			criteria = createCriteria();
		if (orderBy != null) {
			String[] orderFields = orderBy.split(",");
			for (String orderField : orderFields) {
				String field = null;
				boolean isAsc = true;
				String[] orderString = orderField.split(" ");
				if (orderString.length > 0) {
					field = orderString[0];
				}
				if (orderString.length > 1 && "DESC".equalsIgnoreCase(orderString[1])) {
					isAsc = false;
				}
				if (field != null && field.length() > 0) {
					if (isAsc) {
						criteria.addOrder(Order.asc(field));
					} else {
						criteria.addOrder(Order.desc(field));
					}
				}
			}
		}
		return criteria;
	}

	/**
	 * 去除hql的select 子句，未考虑union的情况,用于pagedQuery.
	 * 
	 * @see #pagedQuery(String,long,long,Object[])
	 */
	private static String removeSelect(String hql) {
		Assert.hasText(hql);
		int beginPos = hql.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos != -1, " hql : " + hql + " must has a keyword 'from'");
		return hql.substring(beginPos);
	}

	/**
	 * 去除hql的orderby 子句，用于pagedQuery.
	 * 
	 * @see #pagedQuery(String,long,long,Object[])
	 */
	private static String removeOrders(String hql) {
		Assert.hasText(hql);
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}

	@Override
	public List<T> findAll(String orderFields, long... rowStartIdxAndCount) {
		return this.findAll("", orderFields, rowStartIdxAndCount);
	}

	@Override
	public List<T> findAll(String fieldList, String orderFields, long... rowStartIdxAndCount) {
		
		// 是否要忽略有效性检查
		boolean ignoreValidState = (validExpr==null);
		final Map<String, Object> paramMap = new HashMap<String, Object>();
		final Map<String, String> joinMap = new HashMap<String, String>();
		StringBuffer condition = new StringBuffer("");

		final String queryString = getJpql(orderFields, ignoreValidState, joinMap, condition);
		
		return (List<T>)entityManager.executeFind(executeFind(queryString, paramMap, rowStartIdxAndCount));
	}

	private void setCondition1(String propertyName, Object value,
			StringBuffer condition, final Map<String, Object> paramMap, Map<String, String> joinMap, int joinId) {

		if ("departs".endsWith(propertyName)){
			log.debug("正在调试 departId 处理方式！");
			if (value == null) {
				condition.append("" + modelName + ".").append(
						propertyName).append(" is null");
			} else {
				String joinObj = "o"+(++joinId);
				joinMap.put(propertyName, joinObj);
				condition.append("" + joinObj + ".").append(
						"id").append(" = :").append(
						propertyName);
				paramMap.put(propertyName, value);
			}
			
		}else{
			if (value == null) {
				condition.append("" + modelName + ".").append(
						propertyName).append(" is null");
			} else {
				condition.append("" + modelName + ".").append(
						propertyName).append(" = :").append(
						propertyName);
				paramMap.put(propertyName, value);
			}
		}
	}

	private String getJpql(final String orderFields, boolean ignoreValidState, Map<String, String> joinMap, StringBuffer condition) {
		// 不想忽略有效状态检查
		if (!ignoreValidState){
			if (condition.length() > 0) {
				condition.insert(0, "(").append(") and (").append(validExpr).append(")");
			}else{
				condition.append(validExpr);
			}
		}

		StringBuffer joinString = new StringBuffer();
		if (joinMap.size()>0){
			joinString.append(" join ");
			boolean first1 = true;
			for (String key:joinMap.keySet()){
				if (first1){
					first1 = false;
				}else{
					joinString.append(",");
				}
				joinString.append(modelName).append(".").append(key).append(" ").append(joinMap.get(key));
			}
		}

		if (condition.length()>0){
			condition.insert(0, " where ");
		}
		
		if (orderFields != null && orderFields.length() > 0) {
			condition.append(" order by ");
			boolean first = true;
			for (String orderField:orderFields.split(",")){
				if (first){
					first = false;
				}else{
					condition.append(",");
				}
				condition.append(modelName).append(".").append(orderField);
			}
		}
	
		final String queryString = "select " + modelName + " from "
				+ entityClass.getSimpleName() + " " + modelName + ""
				+ joinString.toString()
				+ condition.toString();
		return queryString;
	}

	@Override
	public String getPKName() {
		return getPKName(entityClass);
	}
	
	public static String getPKName(Class entityClass) {
		String pkName = null;
		
		for (Field field : entityClass.getDeclaredFields()) {
			String fieldName = field.getName();
			Annotation[] annotations = field.getAnnotations();
			if (annotations.length <= 0) {
				String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
				try {
					Method method = entityClass.getDeclaredMethod(methodName);
					annotations = method.getAnnotations();
				} catch (Exception e) {
					// do nothing.
				}
			}
			for (Annotation anno : annotations) {
				if (anno.toString().contains("@javax.persistence.Id()"))
					pkName = fieldName;
					break;
			}
			if (pkName!=null){
				break;
			}
		}
		
		return pkName;
	}

	/** 
     * 去除hql的select 子句，未考虑union的情况,用于pagedQuery. 
     * 
     * @see #pagedQuery(String,int,int,Object[]) 
     */ 
    private static String removeSelect(String hql) { 
        Assert.hasText(hql); 
        int beginPos = hql.toLowerCase().indexOf("from"); 
        Assert.isTrue(beginPos != -1, " hql : " + hql + " must has a keyword 'from'"); 
        return hql.substring(beginPos); 
    } 

	@Override
	public Page<T> find(Page<T> page) {

		T example = page.getFindByExample();
		
		String jpql = (String)page.getCondition().get(IBaseDao.JPQL);
		Object[] values = (Object[])page.getCondition().get(IBaseDao.VALUES);
		
		String orderBy = page.getOrderFields();
		String propertyList = (String)page.getCondition().get(IBaseDao.PROPERTY_LIST);
				
		if (example==null){
			if (jpql!=null){
			       // Count查询 
			       String countQueryString = " select count (*) " + removeSelect(jpql); 
			       long totalCount = this.getCount(page.getCondition());
			       page.setTotalCount(totalCount);

			       if (totalCount > 0){
			           Query query = createQuery(jpql, values); 
			           
			           if (page.getStartIndex()>0){
			        	   query.setFirstResult((int)page.getStartIndex());
			           }
			           
			           if (page.getPageSize()>0){
			        	   query.setMaxResults((int)page.getPageSize());
			           }
			           
			           List<T> list = (List<T>)query.getResultList(); 
			           
			           page.setData(list);
			       }
			}else{
				page.setTotalCount(this.getAllCount());
				List<T> list = this.findAll(orderBy,page.getStartIndex(),page.getPageSize());
				page.setData(list);
			}
		}else{
//			addExampleQuery(query, example, propertyList);
//			this.addOrder(query, orderBy);
//			page.setTotalCount(this.getCount(query));
			List<T> list;
			try {
				list = this.findByExample(example, propertyList, orderBy, page.getStartIndex(),page.getPageSize());
			} catch (Exception e) {
				list = new ArrayList<T>();
			}
			page.setData(list);
		}
		
		return page;
	}

	@Override
	public List<T> findAll() {
		log.info("finding all " + entityClass.getSimpleName() + " instances");
		final String queryString = "select " + modelName + " from " + entityClass.getSimpleName() + " " + modelName;
		Query query = entityManager.createQuery(queryString);
		return query.getResultList();
	}

	@Override
	public PK getPK(T entity) {
		Assert.notNull(entity);
		Assert.notNull(entityClass);
		PK id;
		try {
			id = (PK) PropertyUtils.getProperty(entity, getPKName());
		} catch (Exception e) {
			id = null;
		}
		return id;
	}

	@Override
	public void callProc(final String procName, final Object... values) throws SQLException {
		Query query = entityManager.createNativeQuery(procName);

		for (int i=0;i<values.length;i++) {
			query.setParameter(i, values[i]);
		}
		
		query.executeUpdate();
	}

	@Override
	public long getCount(String hqlCondition) {
		String hql = "select count(*) from "+entityClass.getSimpleName()+" as o where "+hqlCondition;
		Query query = entityManager.createQuery(hql);
		List<Object> result = query.getResultList();
		
		return (Long)(result.get(0));
	}

}